#include "VariableDeclarationBuilder.h"
#include "ScriptCompiler/ScriptFileContext.h"

#include "ScriptCompiler/AST/DecoratorNode.h"

#include "ScriptCompiler/ASTBuilder/TypeBuilder.h"
#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"

CVariableDecalarationBuilder::CVariableDecalarationBuilder(std::shared_ptr<CScriptFileContext> InContext)
{
    Context=InContext;
    Result= std::make_shared<CVariableDeclarationNode>(InContext);
}

bool CVariableDecalarationBuilder::ReadCurentToken()
{
    //read decorator
    if(Context->GetCurrentToken()->Type==EScriptTokenType::Const)
    {
        auto Decorator=std::make_shared<CDecoratorNode>(Context);
        Decorator->AddDecorator(Context->GetCurrentToken());
        Result->Decorator=Decorator;
        Context->CurrentTokenIndex++;
    }

    //read type
    auto TypeBuilder=std::make_shared<CTypeBuilder>(Context);
    if(!TypeBuilder->ReadCurentToken())
    {
        return false;
    }
    Result->Type=TypeBuilder->Result;

    //read name
    {
        auto Name=Context->GetCurrentToken();
        if(Name->Type!=EScriptTokenType::Identifier)
        {
            Context->RaiseError(10168,Name,U"Expecting identifier");
            return false;
        }
        Result->Name=Name;
        Context->CurrentTokenIndex++;
    }

    //return if current token is ';'
    if(Context->GetCurrentToken()->Type==EScriptTokenType::Semicolon)
    {
        //skip ';'
        Context->CurrentTokenIndex++;
        return true;
    }
    
    //check next token , next token should be '=' 
    {
        auto NextToken=Context->GetCurrentToken();
        if(NextToken->Type!=EScriptTokenType::Assign)
        {
            Context->RaiseError(10169,NextToken,U"Expecting '=' or ';' after variable decalaration ");
            return false;
        }

        //skip '='
        Context->CurrentTokenIndex++;

        //find next ';'
        auto SemiColonIndex=FindNextToken(EScriptTokenType::Semicolon,Context);
        if(!SemiColonIndex.HasValue())
        {
            Context->RaiseError(10170,Context->GetCurrentToken(),U"Expecting ';' after variable decalaration ");
            return false;
        }

        //read expression
        auto ExpressionBuilder=std::make_shared<CExpressionBuilder>(Context,SemiColonIndex.GetValue());
        if(!ExpressionBuilder->ReadCurentToken())
        {
            return false;
        }

        Result->InitialValue=ExpressionBuilder->Result;

        //skip ';'
        assert(Context->GetCurrentToken()->Type==EScriptTokenType::Semicolon);
        Context->CurrentTokenIndex++;
    }

    return true;
}

bool CVariableDecalarationBuilder::DetectVariableDeclaration(std::shared_ptr<CScriptFileContext> InContext)
{
    int64_t CurrentPosition=InContext->CurrentTokenIndex;

    //skip decorator
    if(InContext->GetCurrentToken()->Type==EScriptTokenType::Const)
    {
        CurrentPosition++;
    }

    //Detect Type
    CurrentPosition= CTypeBuilder::DetectType(InContext,CurrentPosition);
    if(CurrentPosition==-1)
    {
        return false;
    }

    //Detect Name
    if(InContext->GetToken(CurrentPosition)->Type!=EScriptTokenType::Identifier)
    {
        return false;
    }

    return true;
}